package top.chenyaohua.context;

import org.objectweb.asm.Type;
import top.chenyaohua.annotion.Action;
import top.chenyaohua.annotion.Path;
import top.chenyaohua.annotion.Repository;
import top.chenyaohua.asm.AnnotationMetadata;
import top.chenyaohua.asm.BeanMetadata;
import top.chenyaohua.asm.FieldMetadata;
import top.chenyaohua.asm.MethodMetadata;
import top.chenyaohua.beans.*;
import top.chenyaohua.proxy.JdbcProxy;
import top.chenyaohua.utils.AnnotationUtils;
import top.chenyaohua.utils.PropertieUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

public class BeanFactory {
    /**
     * 创建的beans
     */
    private static HashMap<String, Object> beans = new HashMap<>();

    private static List<BeanMetadata> beanMetadata;

    private static Properties properties;
    /**
     * 准备创建的bean信息
     */
    private static List<BeanMetadata> readyBean = new ArrayList<>();

    static {
        properties = PropertieUtils.getProperties("annotation.properties");

    }

    public static HashMap<String, Object> getBeans () {
        return beans;
    }

    public static void setBeans (HashMap<String, Object> beans) {
        BeanFactory.beans = beans;
    }

    public static List<BeanMetadata> getBeanMetadata () {
        return beanMetadata;
    }

    public static void setBeanMetadata (List<BeanMetadata> beanMetadata) {
        BeanFactory.beanMetadata = beanMetadata;
    }

    //创建对象
    public static void register () {

        for (BeanMetadata beanMetadatum : beanMetadata) {

            register (beanMetadatum);

        }

        System.out.println();
    }

    /**
     * @param beanMetadatum
     */
    private static void register (BeanMetadata beanMetadatum) {
        String className = beanMetadatum.getClassName();
        //如果已经创建直接返回
        if(beans.get(className)!=null){
            return;
        }

        String property = properties.getProperty("create.bean");
        String[] split = property.split(",");

        List<AnnotationMetadata> annotations = beanMetadatum.getAnnotations();
        List<FieldMetadata> fieldMetadatas = beanMetadatum.getFieldMetadata();

        /*
            如果已经存在则表明循环依赖
         */
        if(readyBean.contains(beanMetadatum)  ){
            return;
        }

        //如果不存在
        readyBean.add(beanMetadatum);

    /*    if (fieldMetadatas == null ){
            return;
        }*/

        //先创建依赖对象
        for (FieldMetadata fieldMetadata:Optional.ofNullable(fieldMetadatas).orElseGet(ArrayList::new)) {
            String type = fieldMetadata.getType();
            String cn = Type.getType(type).getClassName();
            if (!isInject(fieldMetadata.getAnnotations())) {
                continue;
            }

            Object o = beans.get(cn);
            if(o != null){
                continue;
            }

            BeanMetadata bm = findBeanMetadataByClassName(cn);

            if(bm == null){
                throw new RuntimeException(String.format("不能创建%s",cn));
            }

            register(bm);
        }



        //
        for (int i = 0; i < annotations.size(); i++) {

            AnnotationMetadata annotationMetadata = annotations.get(i);
            String annotationType = annotationMetadata.getAnnotationType();

            boolean createBean = isCreateBean(split, annotationType);
            List<MethodMetadata> methodMetadata = beanMetadatum.getMethodMetadata();
            boolean noArgumentsConstructor = isNoArgumentsConstructor(methodMetadata);
            try {
                Class<?> aClass = Class.forName(className);
                if (createBean && noArgumentsConstructor ) {

                    if (aClass.isEnum()  || aClass.isAnnotation() || Modifier.isAbstract(aClass.getModifiers())) {
                        throw new Exception(String.format("该类%s不能被创建",className));
                    }
                    //创建对象
                    Object o = aClass.newInstance();

                    for (FieldMetadata fieldMetadata :Optional.ofNullable(fieldMetadatas).orElseGet(ArrayList::new) ) {

                        List<AnnotationMetadata> fieldAnn = fieldMetadata.getAnnotations();
                        //判断该属性是否需要注入
                        if( !isInject(fieldAnn) ){
                            continue;
                        }

                        String name = fieldMetadata.getName();
                        String type = fieldMetadata.getType();
                        String cn = Type.getType(type).getClassName();

                        Field field = aClass.getDeclaredField(name);
                        field.setAccessible(true);
                        field.set(o,beans.get(cn));

                    }
                    beans.put(className, o) ;

                    //如果实例创建成功 查看该类是否是action类  如果是action类生成RequestMapper信息
                    List<AnnotationMetadata> ac = beanMetadatum.getAnnotations();


                    if( AnnotationUtils.isExist(ac, Action.class.getName()) ){

                        AnnotationMetadata actionPath = AnnotationUtils.getAnnotationMetadata(ac, Path.class.getName());
                        Optional<AnnotationMetadata> optional = Optional.ofNullable(actionPath);

                        String path = "";
                        if (optional.isPresent()) {
                            path = (String) optional.get().getParams().get("value");
                        }

                        for (MethodMetadata method : methodMetadata) {

                            //如果是构造方法直接跳过
                            if("<init>".equals(method.getName())){
                                continue;
                            }

                            AnnotationMetadata md = AnnotationUtils.getAnnotationMetadata(method.getAnnotations(), Path.class.getName());
                            if(md == null){
                                continue;
                            }

                            Map<String, Object> params = md.getParams();
                            String  value = (String ) params.get("value");

                            String methodPath = String.join("/",String.join("/", path,value).split("/"));

                            RequestMapper rmp = new RequestMapper("/"+methodPath,method,aClass);

                            RequestMapperHandler.setRequestMapper(rmp);
                        }

                    }

            }else if(createBean && !noArgumentsConstructor ){
                    AnnotationMetadata md = AnnotationUtils.getAnnotationMetadata(annotations, Repository.class.getName());

                    if(aClass.isInterface() && md != null){
                        JdbcProxy jdbcProxy = new JdbcProxy();
                        beans.put(className, jdbcProxy.getInstall(aClass)) ;
                        return;
                    }
                    /*
                       如果是抽象类或类 没有无参构造方法不能构造实例
                     */
                    throw new RuntimeException(String.format("类%s需要一个无参构造方法", className));

            }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        readyBean.remove(beanMetadatum);
    }

    /**
     *  该属性是否注入
     * @param fieldAnn
     * @return
     */
    private static boolean isInject (List<AnnotationMetadata> fieldAnn) {
        for (AnnotationMetadata annotationMetadata : fieldAnn) {
            String annotationType = annotationMetadata.getAnnotationType();
            String className = Type.getType(annotationType).getClassName();
            if (className.equals(properties.getProperty("field.inject"))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 创建
     * @param cn
     * @return
     */
    private static BeanMetadata findBeanMetadataByClassName (String cn) {
        for (BeanMetadata beanMetadatum : beanMetadata) {
            if (beanMetadatum.getClassName().equals(cn)) {
                return beanMetadatum;
            }
        }
        return null;
    }

    /**
     * 是否存在无参构造函数
     * @param methodMetadatas
     * @return
     */
    private static boolean isNoArgumentsConstructor (List<MethodMetadata> methodMetadatas) {
        for (MethodMetadata methodMetadata : methodMetadatas) {
            String name = methodMetadata.getName();
            Map<Integer, FieldMetadata> params = methodMetadata.getParams();
            if(name.equals("<init>") && !Optional.ofNullable(params).isPresent()){
                return true;
            }
        }

        return false;
    }

    /**
     * 类上是否存在创建bean实例的注解
     * @param createBean
     * @param annotationType
     * @return
     */
    private static boolean isCreateBean (String[] createBean ,String annotationType) {

        for (int k = 0; k < createBean.length; k++) {

            String className = Type.getType(annotationType).getClassName();

            if(className.equals(createBean[k])){

                return true;

            }

        }

        return false;
    }

    /**
     *
     * @param className
     * @return
     */
    public static Object getObject(String className){

        return beans.get(className);
    }
}
